//
//  iTrackGoogleMaps.m
//  iTrack
//
//  Created by Sergei Sevriugin on 6/8/10.
//  Copyright 2010 Bellwood Systems. All rights reserved.
//

#import "iTrackGoogleMaps.h"

@interface ITGoogleMaps (PrivateMethods)

	- (void) waitForData;
	- (void) stopWaitForData;
	- (void) loadData;

	- (void) showError:(NSInteger) index;

	- (GDataServiceGoogleMaps*) mapService;

	- (void) setUserName:(NSString*) name;

	- (void) setUserPassword:(NSString*) password;

	- (NSMutableArray*) maps;
	- (void) setMaps:(NSMutableArray*) obj;

	- (NSMutableArray*) mapsFeatures;
	- (void) setMapsFeatures:(NSMutableArray*) obj;

	- (BOOL*) mapsFeaturesLoaded;
	- (void) setMapsFeaturesLoaded:(BOOL*) obj;

	- (GDataFeedMap*) mapFeed;
	- (void)setMapFeed:(GDataFeedMap*) feed;

	- (NSError*) tnxError;
	- (void)setTnxError:(NSError*) error;

	- (GDataServiceTicket*) mapFeedTicket;
	- (void)setMapFeedTicket:(GDataServiceTicket*) obj;

	- (GDataServiceTicket*) mapEditTicket;
	- (void)setMapEditTicket:(GDataServiceTicket*) obj;

	- (GDataFeedMapFeature*) featureFeed;
	- (void)setFeatureFeed:(GDataFeedMapFeature*) feed;

	- (GDataServiceTicket *)featureFeedTicket;
	- (void)setFeatureFeedTicket:(GDataServiceTicket *)obj;

	- (GDataServiceTicket *)featureEditTicket;
	- (void)setFeatureEditTicket:(GDataServiceTicket *)obj;

	- (void)mapsTicket:(GDataServiceTicket *)ticket finishedWithFeed:(GDataFeedMap *)feed error:(NSError *)error;
	- (void)featuresTicket:(GDataServiceTicket *)ticket finishedWithFeed:(GDataFeedMapFeature *)feed error:(NSError *)error;

	- (void)addMapTicket:(GDataServiceTicket *)ticket finishedWithEntry:(GDataEntryMap *)entry error:(NSError *)error;
	- (void)addFeatureTicket:(GDataServiceTicket *)ticket finishedWithEntry:(GDataEntryMap *)entry error:(NSError *)error;

	- (void)renameMapTicket:(GDataServiceTicket *)ticket finishedWithEntry:(GDataEntryMap *)entry error:(NSError *)error;
	- (void)renameFeatureTicket:(GDataServiceTicket *)ticket finishedWithEntry:(GDataEntryMapFeature *)entry error:(NSError *)error;

	- (void)deleteMapTicket:(GDataServiceTicket *)ticket finishedWithNil:(GDataObject *)nilObj error:(NSError *)error;
	- (void)deleteFeatureTicket:(GDataServiceTicket *)ticket finishedWithNil:(GDataObject *)nilObj error:(NSError *)error;
@end

@implementation ITGoogleMaps

@synthesize activeView;
@synthesize isWaiting;
@synthesize need2wait;

- (void) waitForData {
	if (activeView && [self need2wait] == YES) {
		[self setIsWaiting:YES];
		[activeView waitForData];
	}
}

- (void) stopWaitForData {
	if (activeView && [self isWaiting] == YES) {
		[self setNeed2wait:NO];
		[activeView stopWaitForData];
	}
}
- (void) loadData {
	if (activeView) {
		[activeView loadData];
	}
}

- (BOOL) getError {
	if (tnxError == nil) {
		return NO;
	}
	return YES;
}

- (void) showError:(NSInteger) index {
	if (tnxError == nil || index < 0) {
		return;
	}
	
	NSString		*titleString	= [messages objectAtIndex:index];
	NSString		*messageString	= [tnxError localizedDescription];
	NSString		*moreString		= [tnxError localizedFailureReason] ? [tnxError localizedFailureReason] :
											NSLocalizedString(@"Try add it again.", nil);
	
	messageString					= [NSString stringWithFormat:@"%@. %@", messageString, moreString];
	UIAlertView		*alertView		= [[UIAlertView alloc] initWithTitle:titleString
															message:messageString delegate:self
															cancelButtonTitle:@"Cancel" otherButtonTitles:nil];
	
	[alertView show];
	[alertView release];
}

- (id) initWithUsername:(NSString*)name password:(NSString*)pswd {
	if( self = [super init ] )
	{		
		[self setUserName:name];
		[self setUserPassword:pswd];
		[self setActiveView:nil];
				
		[self setMaps:nil];
		[self setMapsFeatures:nil];
		[self setMapFeed:nil];
		[self setMapFeedTicket:nil];
		[self setMapEditTicket:nil];
		
		[self setFeatureFeed:nil];
		[self setFeatureFeedTicket:nil];
		[self setFeatureEditTicket:nil];
		
		[self setMapsFeaturesLoaded:nil];
		[self setTnxError:nil];
		
		[self setIsWaiting:NO];
		[self setNeed2wait:NO];
		
		mapsFeatureIndex	= -1;
		mapCurrent			= -1;
		
		saveStart			= NO;
		
		messages			= [NSArray arrayWithObjects:				// showError index:
							   @"Error Map Fetching",					// 0
							   @"Error Map Adding",						// 1
							   @"Error Loading Map Features",			// 2
							   @"Error Adding Map Feature",				// 3
							   @"Error Updating Map Feature",			// 4
							   @"Error Saving Map",						// 5
							   @"Error Deleting Map",					// 6
							   @"Error Deleting Feature", nil];			// 7
	}
	return self;
}

- (void)setUserCredentials:(NSString*)name password:(NSString*)pswd {
	[self setUserName:name];
	[self setUserPassword:pswd];
	
	[self fetchMaps];
}


- (void)dealloc {
	
	[self setUserName:nil];
	[self setUserPassword:nil];
	[self setActiveView:nil];
	
	[self setMaps:nil];
	[self setMapsFeatures:nil];
	[self setMapFeed:nil];
	[self setMapFeedTicket:nil];
	[self setMapEditTicket:nil];
	
	[self setFeatureFeed:nil];
	[self setFeatureFeedTicket:nil];
	[self setFeatureEditTicket:nil];
	
	[self setMapsFeaturesLoaded:nil];
	[self setTnxError:nil];
	
	[super dealloc];
}

#pragma mark -

// get a map service object with the current username/password
//
// A "service" object handles networking tasks.  Service objects
// contain user authentication information as well as networking
// state information (such as cookies and the "last modified" date for
// fetched data.)

- (GDataServiceGoogleMaps *)mapService {
	
	static GDataServiceGoogleMaps* service = nil;
	
	if (!service) {
		service = [[GDataServiceGoogleMaps alloc] init];
		
		[service setShouldCacheDatedData:YES];
		[service setServiceShouldFollowNextLinks:YES];
	}
	
	// username/password may change	
	[service setUserCredentialsWithUsername:userName password:userPassword];
	
	return service;
}


#pragma mark Fetch feed of all of the user's maps

// begin retrieving the list of the user's maps
- (void)fetchMaps {
	
	[self setMapFeed:nil];
	
	[self setFeatureFeed:nil];
	[self setFeatureFeedTicket:nil];
	
	[self setMaps:nil];
	[self setMapsFeatures:nil];
	[self setMapsFeaturesLoaded:nil];

	[self setTnxError:nil];
	
	[self setIsWaiting:NO];
	[self setNeed2wait:YES];

	[self waitForData];
	
	GDataServiceGoogleMaps*	service = [self mapService];
	NSURL*					feedURL = [GDataServiceGoogleMaps mapsFeedURLForUserID:kGDataServiceDefaultUser
												projection:kGDataMapsProjectionFull];

	GDataServiceTicket*		ticket = [service fetchFeedWithURL:feedURL
												delegate:self
												didFinishSelector:@selector(mapsTicket:finishedWithFeed:error:)];	
	[self setMapFeedTicket:ticket];

}

// map feed fetch callback
- (void)mapsTicket:(GDataServiceTicket *)ticket finishedWithFeed:(GDataFeedMap *)feed
             error:(NSError *)error {

	[self setNeed2wait:NO];

	[self setMapFeed:feed];
	[self setMapFeedTicket:nil];

	[self setTnxError:error];
		
	if (error) {
	//	[self showError:0];
		
		[self setMapFeed:nil];
		[self setFeatureFeed:nil];
		[self setFeatureFeedTicket:nil];
		
		[self setMaps:nil];
		[self setMapsFeatures:nil];
		[self setMapsFeaturesLoaded:nil];
		
		[self stopWaitForData];
		return;
	}
	
	[self setMaps:[[NSMutableArray alloc] initWithArray:[mapFeed entries]]];
	
	NSInteger				mapsCount = [maps count];
	
	[self setMapsFeatures:[[NSMutableArray alloc] initWithCapacity:mapsCount]];
	
	[self setMapsFeaturesLoaded:malloc(sizeof(BOOL) * mapsCount)];
	
	for (int i = 0; i < mapsCount; i++) {
		
		[[maps objectAtIndex:i] retain];

		NSMutableArray* features = [[NSMutableArray alloc] initWithCapacity:3];
		
		[mapsFeatures addObject:features];
		
		mapsFeaturesLoaded[i] = NO;
	}
	
	[self stopWaitForData];
}

- (void)addMap:(GDataEntryMap *)map {

	NSURL *postURL					= [[mapFeed postLink] URL];
    GDataServiceGoogleMaps *service = [self mapService];
	
	if (postURL) {
		[self setMapEditTicket:nil];
		[self setTnxError:nil];
		
		[self setIsWaiting:NO];
		[self setNeed2wait:YES];
		
		[self waitForData];
		
		GDataServiceTicket *ticket;
		ticket = [service fetchEntryByInsertingEntry:map
											forFeedURL:postURL
											delegate:self
											didFinishSelector:@selector(addMapTicket:finishedWithEntry:error:)];
	
		[self setMapEditTicket:ticket];	
	}
}

- (void)addMapTicket:(GDataServiceTicket *)ticket finishedWithEntry:(GDataEntryMap *)entry
               error:(NSError *)error {
	
	[self setNeed2wait:NO];

	[self setMapEditTicket:nil];
	[self setTnxError:error];
	
	if (error) {
		[self showError:1];
		[self stopWaitForData];
		return;
	}		
	[self fetchMaps];
}

- (unsigned)countOfMaps {
	if (maps == nil) {
		return 0;
	}
	return [maps count];
}


// get the map by index in the map feed
- (GDataEntryMap *)getMapAtIndex:(NSUInteger)index {	
	if (maps == nil) {
		return nil;
	}
	
	int maxIndex	= [maps count];
	
	if (index >= 0 && maxIndex > index) {
		return (GDataEntryMap *) [maps objectAtIndex:index];
	}
	return nil;
}

#pragma mark Fetch a map's features

// for the map selected in the top list, begin retrieving the lists of
// features and tables
- (void)fetchFeaturesOfMap:(NSInteger)mapid {

	// check if already loaded 
	NSMutableArray* features = [mapsFeatures objectAtIndex:mapid]; 
	
	if ([features count] > 0 || mapsFeaturesLoaded[mapid]) {
		
		[self loadData];
		
		return;
	}
	
	GDataEntryMap *map = [self getMapAtIndex:mapid];

	if (map) {
		GDataServiceGoogleMaps *service = [self mapService];
		
		// fetch the feed of features
		NSURL *featuresFeedURL = [map featuresFeedURL];
		if (featuresFeedURL) {
			
			GDataQueryMaps *query;
			
			// fetch all features of the selected map
			query = [GDataQueryMaps mapsQueryWithFeedURL:featuresFeedURL];
			
			[self setFeatureFeed:nil];
			[self setFeatureFeedTicket:nil];
			[self setTnxError:nil];
			
			[self setIsWaiting:NO];
			[self setNeed2wait:YES];
			
			[self waitForData];

			mapCurrent = mapid;
			
			GDataServiceTicket *ticket;
			
			ticket = [service fetchFeedWithQuery:query
								delegate:self
								didFinishSelector:@selector(featuresTicket:finishedWithFeed:error:)];
			
			[self setFeatureFeedTicket:ticket];
		}
	}
}

// features fetch callback
- (void)featuresTicket:(GDataServiceTicket *)ticket finishedWithFeed:(GDataFeedMapFeature *)feed
                 error:(NSError *)error {

	[self setNeed2wait:NO];
	
	[self setFeatureFeed:feed];
	[self setFeatureFeedTicket:nil];
	[self setTnxError:error];

	if (error) {
		[self showError:2];
		[self stopWaitForData];
		return;
	}		
	
	NSMutableArray* features				= [mapsFeatures objectAtIndex:mapCurrent]; 

	if (feed) {
		NSInteger	features_count			= [[feed entries] count];
		for (int i = 0; i < features_count; i++) {
			GDataEntryMapFeature*	feature	= [[feed entries] objectAtIndex:i];	
			[features addObject:[feature retain]];		
			}
	}
	
	mapsFeaturesLoaded[mapCurrent] = YES;
	
	[self stopWaitForData];
}


- (BOOL)featuresOfMapLoaded:(NSInteger)mapid {
	if (mapsFeaturesLoaded) {
		return mapsFeaturesLoaded[mapid];
	}
	else {
		return NO;
	}
}

- (unsigned)countOfFeaturesOfMap:(NSInteger)mapid {
	
	NSMutableArray* features = [mapsFeatures objectAtIndex:mapid];
	
	if (features) {
		return [features count];
	}
	else {
		return 0;
	}
}


// get the feature by index in the feature feed
- (GDataEntryMapFeature *)getFeatureOfMap:(NSInteger)mapid AtIndex:(NSUInteger)index {

	NSMutableArray* features			= [mapsFeatures objectAtIndex:mapid];
	
	if (features == nil) {
		return nil;
	}

	int maxIndex						= [features count];
	
	if (index >= 0 && maxIndex > index) {
		GDataEntryMapFeature *feature	= [features objectAtIndex:index];
		return feature;
	}
	return nil;
}

- (void) deleteFeatureOfMap:(NSInteger)mapid AtIndex:(NSUInteger)index {
	NSMutableArray* features			= [mapsFeatures objectAtIndex:mapid];
	
	if (features == nil) {
		return;
	}
	
	int maxIndex						= [features count];
	
	if (index >= 0 && maxIndex > index) {
		GDataEntryMapFeature *feature	= [features objectAtIndex:index];
		
		[self deleteFeature:feature];
		[features removeObjectAtIndex:index];
	}	
}


- (void) setCurrentMap:(NSInteger)mapid {

	if (mapid < 0 || mapid >= [maps count]) {
		return;
	}

	mapCurrent							= mapid;

	NSMutableArray* features			= [mapsFeatures objectAtIndex:mapid];

	if (features == nil) {
		return;
	}
	
	mapsFeatureIndex					= [features count];
	saveStart							= YES;
	
}

- (void) setCurrentMapFeaturesLoaded:(BOOL)loaded {

	if (mapCurrent < 0 || mapCurrent >= [maps count]) {
		return;
	}
	
	mapsFeaturesLoaded[mapCurrent]		= loaded;
}

- (void) addCurrentMapFeature:(GDataEntryMapFeature *)feature {
	
	if (mapCurrent < 0 || mapCurrent >= [maps count]) {
		return;
	}
	
	NSMutableArray* features			= [mapsFeatures objectAtIndex:mapCurrent];
	
	if (features == nil) {
		return;
	}
	[features addObject:feature];
}


- (void) saveCurrentMapFeatures {
	
	if (mapCurrent < 0 || mapCurrent >= [maps count]) {
		return;
	}
	
	NSMutableArray* features				= [mapsFeatures objectAtIndex:mapCurrent];
	
	if (features == nil) {
		return;
	}
	
	NSInteger		featuresCount			= [features count];
	
	if (featuresCount <= mapsFeatureIndex || mapsFeatureIndex < 0) {
		[self loadData];
		return;
	}
	
	GDataEntryMap				*map		= [self getMapAtIndex:mapCurrent];
	
	if (map) {
		
		NSURL					*postURL	= [map featuresFeedURL];
		GDataServiceGoogleMaps	*service	= [self mapService];
		GDataServiceTicket		*ticket		= nil;
		GDataEntryMapFeature	*feature	= [features objectAtIndex:mapsFeatureIndex];
		ticket = [service fetchEntryByInsertingEntry:feature
											forFeedURL:postURL
											delegate:self
											didFinishSelector:@selector(addFeatureTicket:finishedWithEntry:error:)];
    
		[self setFeatureEditTicket:ticket];
	
		if (saveStart) {
			saveStart = NO;
			
			[self setNeed2wait:YES];
			[self setIsWaiting:NO];
			
			[self waitForData];
		}
	}	
}

- (void)addFeatureTicket:(GDataServiceTicket *)ticket finishedWithEntry:(GDataEntryMap *)entry
                   error:(NSError *)error {
	
	[self setNeed2wait:NO];
	
	[self setFeatureEditTicket:nil];
	[self setTnxError:error];

	if (error) {
		[self showError:3];
		[self stopWaitForData];
		return;
	}		
	
	mapsFeatureIndex++;
	
	NSMutableArray* features			= [mapsFeatures objectAtIndex:mapCurrent];
	
	if (features == nil) {
		return;
	}
	
	NSInteger		featuresCount		= [features count];
	
	if (featuresCount <= mapsFeatureIndex || mapsFeatureIndex < 0) {
		[self stopWaitForData];
	}
	else {
		[self saveCurrentMapFeatures];
	}

}

- (void) updateFeature:(GDataEntryMapFeature *)feature {
	
	if (feature == nil) {
		return;
	}
	
	NSURL *editURL = [[feature editLink] URL];
	
	if (editURL) {
		
		GDataServiceGoogleMaps	*service = [self mapService];
		GDataServiceTicket		*ticket	= nil;
		
		[self setNeed2wait:YES];
		[self setIsWaiting:NO];
		[self waitForData];
		
		ticket = [service fetchEntryByUpdatingEntry:feature
											delegate:self
											didFinishSelector:@selector(renameFeatureTicket:finishedWithEntry:error:)];
		
		[self setFeatureEditTicket:ticket];		
	}
}

- (void)renameFeatureTicket:(GDataServiceTicket *)ticket
          finishedWithEntry:(GDataEntryMapFeature *)entry
                      error:(NSError *)error {
	
	[self setNeed2wait:NO];
	[self setFeatureEditTicket:nil];
	[self setTnxError:error];
	
	if (error) {
		[self showError:4];
		[self stopWaitForData];
		return;
	}
	
	[self stopWaitForData];
}

- (void) updateMap:(NSInteger)mapid {
	
	GDataEntryMap					*map		= [self getMapAtIndex:mapid];
	
	if (map) {
		NSURL						*editURL	= [[map editLink] URL];
	
		if (editURL) {
		
			GDataServiceGoogleMaps	*service	= [self mapService];
		
			GDataServiceTicket *ticket;
			
			[self setNeed2wait:YES];
			[self setIsWaiting:NO];
			[self waitForData];		
			
			
			ticket = [service fetchEntryByUpdatingEntry:map
												delegate:self
												didFinishSelector:@selector(renameMapTicket:finishedWithEntry:error:)];
			[self setMapEditTicket:ticket];			
		}
	}
}

// rename map callback
- (void)renameMapTicket:(GDataServiceTicket *)ticket
					finishedWithEntry:(GDataEntryMap *)entry
					error:(NSError *)error {

	[self setNeed2wait:NO];
	[self setMapEditTicket:nil];
	[self setTnxError:error];
	
	if (error) {
		[self showError:5];
		[self stopWaitForData];
		return;
	}
	
	[self stopWaitForData];
}

- (void) deleteMap:(NSInteger)mapid {
	
	GDataEntryMap					*map		= [self getMapAtIndex:mapid];
	
	if (map) {
		
		GDataServiceGoogleMaps		*service	= [self mapService];
		GDataServiceTicket			*ticket;
	
		[self setNeed2wait:YES];
		[self setIsWaiting:NO];
		
		[self waitForData];
		
		ticket = [service deleteEntry:map
								delegate:self
								didFinishSelector:@selector(deleteMapTicket:finishedWithNil:error:)];
		
		[self setMapEditTicket:ticket];
		
		// save the name in the ticket
		[ticket setProperty:[[map title] stringValue]
					 forKey:@"mapName"];		
	}
}

- (void)deleteMapTicket:(GDataServiceTicket *)ticket
					finishedWithNil:(GDataObject *)nilObj
					error:(NSError *)error {
	
	[self setNeed2wait:NO];
	
	[self setMapEditTicket:nil];
	[self setTnxError:error];
	
	if (error) {
		[self showError:6];
		[self stopWaitForData];
		return;
	}
	
	[self fetchMaps];
}

- (void)deleteFeature:(GDataEntryMapFeature *)feature {
	if (feature == nil) {
		return;
	}
	GDataServiceGoogleMaps	*service = [self mapService];
	GDataServiceTicket		*ticket;
	
	[self setNeed2wait:YES];
	[self setIsWaiting:NO];
	[self waitForData];
	
	ticket = [service deleteEntry:feature
						delegate:self
						didFinishSelector:@selector(deleteFeatureTicket:finishedWithNil:error:)];
	
	[self setFeatureEditTicket:ticket];
		
	// save the name in the ticket
	[ticket setProperty:[[feature title] stringValue] forKey:@"featureName"];	
}

- (void)deleteFeatureTicket:(GDataServiceTicket *)ticket
            finishedWithNil:(GDataObject *)nilObj
                      error:(NSError *)error {
	
	[self setNeed2wait:NO];
	[self setFeatureEditTicket:nil];
	[self setTnxError:error];
	
	if (error) {
		[self showError:7];
		[self stopWaitForData];
		return;
	}
	
	[self stopWaitForData];
}

#pragma mark Setters and Getters

- (NSString*) userName {
	return userName;
}

- (void) setUserName:(NSString*) name {
	[userName release];
	
	userName = [name retain];
}

- (NSString*) userPassword {
	return userPassword;
}

- (void) setUserPassword:(NSString*) password {
	[userPassword release];
	
	userPassword = [password retain];
}

- (NSMutableArray*) maps {
	return maps;
}

- (void) setMaps:(NSMutableArray*) obj {
	
	if (maps) {
		for (int i = 0; i < [maps count] ; i++) {
			[[maps objectAtIndex:i] release];
		}
		[maps release];
	}
	maps = [obj retain];
}

- (NSMutableArray*) mapsFeatures {
	return mapsFeatures;
}

- (void) setMapsFeatures:(NSMutableArray*) obj {
	if (mapsFeatures) {
		for (int i = 0; i < [mapsFeatures count]; i++) {
			NSMutableArray* features = [mapsFeatures objectAtIndex:i];
			
			if (features) {
				for (int j = 0; j < [features count]; j++) {
					[[features objectAtIndex:j] release];
				}
			[features release];
			}
		}
		[mapsFeatures release];
	}
	mapsFeatures = [obj retain];
}

- (BOOL*) mapsFeaturesLoaded {
	return mapsFeaturesLoaded;
}

- (void) setMapsFeaturesLoaded:(BOOL*) obj {
	
	if (mapsFeaturesLoaded) {
		free(mapsFeaturesLoaded);
	}
	mapsFeaturesLoaded = obj;
}

- (GDataFeedMap*) mapFeed {
	return mapFeed;
}

- (void)setMapFeed:(GDataFeedMap*) feed {
	[mapFeed release];
	
	mapFeed = [feed retain];
}

- (NSError *)tnxError {
	return tnxError;
}

- (void)setTnxError:(NSError *)error {
	[tnxError release];
	
	tnxError = [error retain];
}

- (GDataServiceTicket *)mapFeedTicket {
	return mapFeedTicket;
}

- (void)setMapFeedTicket:(GDataServiceTicket *)obj {
	[mapFeedTicket release];

	mapFeedTicket = [obj retain];
}

- (GDataServiceTicket *)mapEditTicket {
	return mapEditTicket;
}

- (void)setMapEditTicket:(GDataServiceTicket *)obj {
	[mapEditTicket release];

	mapEditTicket = [obj retain];
}


- (GDataFeedMapFeature *)featureFeed {
	return featureFeed;
}

- (void)setFeatureFeed:(GDataFeedMapFeature *)feed {
	[featureFeed release];

	featureFeed = [feed retain];
}


- (GDataServiceTicket *)featureFeedTicket {
	return featureFeedTicket;
}

- (void)setFeatureFeedTicket:(GDataServiceTicket *)obj {
	[featureFeedTicket release];

	featureFeedTicket = [obj retain];
}

- (GDataServiceTicket *)featureEditTicket {
	return featureEditTicket;
}

- (void)setFeatureEditTicket:(GDataServiceTicket *)obj {
	[featureEditTicket release];

	featureEditTicket = [obj retain];
}

@end
